﻿using Sidvall.Business;

namespace Sidvall.UI.Entities
{
    public sealed class TreeItem : BusinessBase, Sidvall.Data.IExtendedChildItem
    {
        #region Public Members

        #region Id

        private string _Id;
        public string Id
        {
            get
            {
                return _Id;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_Id == value)
                    return;
                _Id = value;
                OnPropertyChanged(nameof(Id), true, true);
            }
        }

        #endregion
        #region FullName

        private string _FullName;
        public string FullName
        {
            get
            {
                return _FullName;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_FullName == value)
                    return;
                _FullName = value;
                OnPropertyChanged(nameof(FullName), true, false);
            }
        }

        #endregion
        #region Name

        private string _Name;
        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_Name == value)
                    return;
                _Name = value;
                OnPropertyChanged(nameof(Name), true, true);
            }
        }

        #endregion
        #region Code

        private string _Code;
        public string Code
        {
            get
            {
                return _Code;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_Code == value)
                    return;
                _Code = value;
                OnPropertyChanged(nameof(Code), true, true);
            }
        }

        #endregion
        #region Description

        private string _Description;
        public string Description
        {
            get
            {
                return _Description;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_Description == value)
                    return;
                _Description = value;
                OnPropertyChanged(nameof(Description), true, true);
            }
        }

        #endregion
        #region GroupName

        private string _GroupName;
        public string GroupName
        {
            get
            {
                return _GroupName;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_GroupName == value)
                    return;
                _GroupName = value;
                OnPropertyChanged(nameof(GroupName), true, true);
            }
        }

        #endregion
        #region ParentId

        private string _ParentId;
        public string ParentId
        {
            get
            {
                return _ParentId;
            }
            set
            {
                value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
                if (_ParentId == value)
                    return;
                _ParentId = value;
                OnPropertyChanged(nameof(ParentId), true, true);
            }
        }

        #endregion
        #region DisplayPosition

        private int _DisplayPosition;
        public int DisplayPosition
        {
            get
            {
                return _DisplayPosition;
            }
            set
            {
                if (_DisplayPosition == value)
                    return;
                _DisplayPosition = value;
                OnPropertyChanged(nameof(DisplayPosition), true, true);
            }
        }

        #endregion
        #region IsEnabled

        private bool _IsEnabled;
        public bool IsEnabled
        {
            get
            {
                return _IsEnabled;
            }
            set
            {
                if (_IsEnabled == value)
                    return;
                _IsEnabled = value;
                OnPropertyChanged(nameof(IsEnabled), true, true);
            }
        }

        #endregion
        #region IsExpanded

        private bool _IsExpanded;
        public bool IsExpanded
        {
            get
            {
                return _IsExpanded;
            }
            set
            {
                if (_IsExpanded == value)
                    return;
                _IsExpanded = value;
                OnPropertyChanged(nameof(IsExpanded), true, true);
            }
        }

        #endregion
        #region IsLoaded

        private bool _IsLoaded = true;
        public bool IsLoaded
        {
            get
            {
                return _IsLoaded;
            }
            set
            {
                if (_IsLoaded == value)
                    return;
                _IsLoaded = value;
                if (!_IsLoaded)
                {
                    var childItems = new TreeItemCollection();
                    childItems.Add(null);
                    this.ChildItems = childItems;
                }
                OnPropertyChanged(nameof(IsLoaded), true, true);
            }
        }

        #endregion
        #region IsSelected

        private bool _IsSelected;
        public bool IsSelected
        {
            get
            {
                return _IsSelected;
            }
            set
            {
                PropagateDownSelected(value, true, true, true);
            }
        }

        #endregion
        #region IsSelectable

        private bool _IsSelectable = true;
        public bool IsSelectable
        {
            get
            {
                return _IsSelectable;
            }
            set
            {
                if (_IsSelectable == value)
                    return;
                _IsSelectable = value;
                OnPropertyChanged(nameof(IsSelectable), true, true);
            }
        }

        #endregion
        #region IsVisible

        private bool _IsVisible = true;
        public bool IsVisible
        {
            get
            {
                return _IsVisible;
            }
            set
            {
                PropagateDownVisible(value, true, true, true);
            }
        }

        #endregion
        #region SelectedBehavior

        private TreeItemSelectedBehavior _SelectedBehavior = TreeItemSelectedBehavior.None;
        public TreeItemSelectedBehavior SelectedBehavior
        {
            get
            {
                return _SelectedBehavior;
            }
            set
            {
                if (_SelectedBehavior == value)
                    return;
                _SelectedBehavior = value;
                OnPropertyChanged(nameof(SelectedBehavior), true, true);
            }
        }

        #endregion
        #region VisibleBehavior

        private TreeItemVisibleBehavior _VisibleBehavior = TreeItemVisibleBehavior.PropagateDown;
        public TreeItemVisibleBehavior VisibleBehavior
        {
            get
            {
                return _VisibleBehavior;
            }
            set
            {
                if (_VisibleBehavior == value)
                    return;
                _VisibleBehavior = value;
                OnPropertyChanged(nameof(VisibleBehavior), true, true);
            }
        }

        #endregion
        #region ChildItems

        private TreeItemCollection _ChildItems;
        public TreeItemCollection ChildItems
        {
            get
            {
                return _ChildItems;
            }
            private set
            {
                UpdateChildProperty(_ChildItems, value);
                _ChildItems = value;
            }
        }

        #endregion
        #region DataItem

        private object _DataItem;
        public object DataItem
        {
            get
            {
                return _DataItem;
            }
            set
            {
                _DataItem = value;
                OnPropertyChanged(nameof(DataItem), true, false);
            }
        }

        #endregion
        #region TotalChildCount

        public int TotalChildCount
        {
            get
            {
                if (this.ChildItems == null)
                    return 0;
                return this.ChildItems.TotalChildCount;
            }
        }

        #endregion
        #region DataRowContextStorageKey

        protected override string DataRowContextStorageKey
        {
            get
            {
                return this.Id;
            }
        }

        #endregion

        #region AcceptChanges

        public void AcceptChanges()
        {
            AcceptChanges(null);
        }
        public void AcceptChanges(Sidvall.Data.AcceptChangesSettings acceptChangesSettings)
        {
            if (this.DataRowStateIsDirty)
            {
                if (acceptChangesSettings == null)
                    acceptChangesSettings = new Sidvall.Data.AcceptChangesSettings();
                Sidvall.Data.EntityManager.AcceptItemChanges(this, acceptChangesSettings);
                if (this.ChildItems != null)
                    this.ChildItems.AcceptChanges(acceptChangesSettings);
            }
        }

        #endregion
        #region Clone

        public TreeItem Clone()
        {
            var copySettings = Sidvall.Data.CopySettings.Clone();
            return Copy(copySettings);
        }

        #endregion
        #region Copy

        public TreeItem Copy()
        {
            return Copy(null);
        }
        public TreeItem Copy(Sidvall.Data.CopySettings copySettings)
        {
            if (copySettings == null)
                copySettings = new Sidvall.Data.CopySettings();
            if (!Sidvall.Data.DataRowContextManager.IsValid(this, copySettings.RowFilter))
                return null;
            var item = new TreeItem();
            item.Import(this, copySettings);
            return item;
        }

        #endregion
        #region Import

        public void Import(TreeItem item)
        {
            Import(item, null);
        }
        public void Import(TreeItem item, Sidvall.Data.CopySettings copySettings)
        {
            if (item == null)
                return;
            if (copySettings == null)
                copySettings = new Sidvall.Data.CopySettings();
            if (copySettings.FieldCopyOption != Business.FieldCopyOptions.None)
            {
                _Id = item.Id;
                _FullName = item.FullName;
                _Name = item.Name;
                _Code = item.Code;
                _Description = item.Description;
                _GroupName = item.GroupName;
                _ParentId = item.ParentId;
                _DisplayPosition = item.DisplayPosition;
                _IsEnabled = item.IsEnabled;
                _IsExpanded = item.IsExpanded;
                _IsLoaded = item.IsLoaded;
                _IsSelected = item.IsSelected;
                _IsSelectable = item.IsSelectable;
                _IsVisible = item.IsVisible;
                _SelectedBehavior = item.SelectedBehavior;
                _VisibleBehavior = item.VisibleBehavior;
                _DataItem = item.DataItem;
            }
            if (copySettings.IncludeChildren)
            {
                if (_ChildItems != null)
                    _ChildItems.Import(item.ChildItems, copySettings);
                else if (item.ChildItems != null)
                    _ChildItems = item.ChildItems.Copy(copySettings);
            }
            Sidvall.Data.EntityManager.AfterImportItem(this, item, copySettings);
        }

        #endregion
        #region InitializeDefaultValues

        public void InitializeDefaultValues()
        {
            this.Id = string.Empty;
            this.Name = string.Empty;
            this.ParentId = string.Empty;
            this.GroupName = string.Empty;
            this.Code = string.Empty;
            this.Description = string.Empty;
            this.DisplayPosition = 0;
            this.IsSelected = true;
        }

        #endregion
        #region DataRowStateIsDirty

        public override bool DataRowStateIsDirty
        {
            get
            {
                if (this.DataRowStateIsSelfDirty)
                    return true;
                if ((this.ChildItems != null) && (this.ChildItems.DataRowStateIsDirty))
                    return true;
                return false;
            }
        }

        #endregion
        #region LoadChildItems

        public void LoadChildItems(TreeItemCollection item)
        {
            this.ChildItems = item;
            OnPropertyChanged(nameof(ChildItems), true, false);
        }

        #endregion
        #region ToString

        public override string ToString()
        {
            if (!string.IsNullOrWhiteSpace(this.FullName))
                return this.FullName;
            return this.Name;
        }

        #endregion

        #region OnChildChanged

        protected override void OnChildChanged(ChildChangedEventArgs args)
        {
            if (!_DisabledPropagateUpSelected)
            {
                if (args?.PropertyChangedArgs?.PropertyName == nameof(IsSelected))
                {
                    switch (this.SelectedBehavior)
                    {
                        case TreeItemSelectedBehavior.PropagateUpSelected:
                        case TreeItemSelectedBehavior.PropagateDownAndUpSelected:
                        case TreeItemSelectedBehavior.PropagateDownNotSelectedAndUpSelected:
                            if (this.ChildItems != null)
                            {
                                var selected = false;
                                int iMax = this.ChildItems.Count;
                                for (int i = 0; i < iMax; i++)
                                {
                                    if (this.ChildItems[i].IsSelected)
                                    {
                                        selected = true;
                                        break;
                                    }
                                }
                                if (selected)
                                {
                                    _DisabledPropagateDownSelected = true;
                                    this.IsSelected = selected;
                                    _DisabledPropagateDownSelected = false;
                                }
                            }
                            break;
                        case TreeItemSelectedBehavior.PropagateUp:
                        case TreeItemSelectedBehavior.PropagateDownAndUp:
                        case TreeItemSelectedBehavior.PropagateDownNotSelectedAndUp:
                            if (this.ChildItems != null)
                            {
                                var selected = false;
                                int iMax = this.ChildItems.Count;
                                for (int i = 0; i < iMax; i++)
                                {
                                    if (this.ChildItems[i].IsSelected)
                                    {
                                        selected = true;
                                        break;
                                    }
                                }
                                _DisabledPropagateDownSelected = true;
                                this.IsSelected = selected;
                                _DisabledPropagateDownSelected = false;
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
            base.OnChildChanged(args);
        }

        #endregion

        #endregion
        #region Private Members

        private bool _DisabledPropagateDownSelected;
        private bool _DisabledPropagateUpSelected;

        #region CreateTreeItem

        internal static TreeItem CreateTreeItem(string id, string name, string parentId, int displayPosition, bool isSelected, object dataItem)
        {
            var item = new TreeItem();
            item._Id = Sidvall.Business.ValidationManager.GetValidString(id, false, null);
            item._Name = Sidvall.Business.ValidationManager.GetValidString(name, false, null);
            item._ParentId = Sidvall.Business.ValidationManager.GetValidString(parentId, false, null);
            item._DisplayPosition = displayPosition;
            item._IsSelected = isSelected;
            item._DataItem = dataItem;
            return item;

        }
        internal static TreeItem CreateTreeItem(string id, string name, string parentId, int displayPosition, bool isSelected, object dataItem, string groupName, string code,
            string description)
        {
            var item = CreateTreeItem(id, name, parentId, displayPosition, isSelected, dataItem);
            item._GroupName = Sidvall.Business.ValidationManager.GetValidString(groupName, false, null);
            item._Code = Sidvall.Business.ValidationManager.GetValidString(code, false, null);
            item._Description = Sidvall.Business.ValidationManager.GetValidString(description, false, null);
            return item;
        }

        #endregion
        #region PropagateDownSelected

        internal void PropagateDownSelected(bool isSelected, bool raisePropertyChanged, bool updateState, bool setItemValue)
        {
            if (setItemValue)
            {
                if (!_IsSelectable)
                    return;
                if (_IsSelected == isSelected)
                    return;
                _IsSelected = isSelected;
                OnPropertyChanged(nameof(IsSelected), raisePropertyChanged, updateState);
            }
            if (!_DisabledPropagateDownSelected)
            {
                switch (this.SelectedBehavior)
                {
                    case TreeItemSelectedBehavior.PropagateDown:
                    case TreeItemSelectedBehavior.PropagateDownAndUp:
                    case TreeItemSelectedBehavior.PropagateDownAndUpSelected:
                        if (this.ChildItems != null)
                        {
                            _DisabledPropagateUpSelected = true;
                            this.ChildItems.PropagateDownSelected(isSelected, raisePropertyChanged, updateState);
                            _DisabledPropagateUpSelected = false;
                        }
                        break;
                    case TreeItemSelectedBehavior.PropagateDownNotSelected:
                    case TreeItemSelectedBehavior.PropagateDownNotSelectedAndUp:
                    case TreeItemSelectedBehavior.PropagateDownNotSelectedAndUpSelected:
                        if ((this.ChildItems != null) && (!isSelected))
                        {
                            _DisabledPropagateUpSelected = true;
                            this.ChildItems.PropagateDownSelected(isSelected, raisePropertyChanged, updateState);
                            _DisabledPropagateUpSelected = false;
                        }
                        break;
                    default:
                        break;
                }
            }
        }

        #endregion
        #region PropagateDownVisible

        internal void PropagateDownVisible(bool isVisible, bool raisePropertyChanged, bool updateState, bool setItemValue)
        {
            if (setItemValue)
            {
                if (_IsVisible == isVisible)
                    return;
                _IsVisible = isVisible;
                OnPropertyChanged(nameof(IsVisible), raisePropertyChanged, updateState);
            }
            switch (this.VisibleBehavior)
            {
                case TreeItemVisibleBehavior.PropagateDown:
                    if (this.ChildItems != null)
                    {
                        this.ChildItems.PropagateDownVisible(isVisible, raisePropertyChanged, updateState);
                    }
                    break;
                default:
                    break;
            }
        }

        #endregion
        #region SetFullName

        internal void SetFullName(bool raisePropertyChanged, bool updateState, string fullName)
        {
            fullName = Sidvall.Business.ValidationManager.GetValidString(fullName, false, null);
            if (fullName != _FullName)
            {
                _FullName = fullName;
                OnPropertyChanged(nameof(FullName), raisePropertyChanged, updateState);
            }
        }

        #endregion
        #region SetSelectedBehavior

        internal void SetSelectedBehavior(bool raisePropertyChanged, bool updateState, TreeItemSelectedBehavior selectedBehavior)
        {
            if (selectedBehavior != _SelectedBehavior)
            {
                _SelectedBehavior = selectedBehavior;
                OnPropertyChanged(nameof(SelectedBehavior), raisePropertyChanged, updateState);
            }
        }

        #endregion
        #region SetValues

        internal void SetValues(bool invertedValues, bool raisePropertyChanged, bool updateState, bool? isEnabled, bool? isExpanded, bool? isSelected, bool? isSelectable,
            bool? isVisible)
        {
            if (!invertedValues)
            {
                if ((isEnabled != null) && (isEnabled.Value != _IsEnabled))
                {
                    _IsEnabled = isEnabled.Value;
                    OnPropertyChanged(nameof(IsEnabled), raisePropertyChanged, updateState);
                }
                if ((isExpanded != null) && (isExpanded.Value != _IsExpanded))
                {
                    _IsExpanded = isExpanded.Value;
                    OnPropertyChanged(nameof(IsExpanded), raisePropertyChanged, updateState);
                }
                if ((isSelected != null) && (isSelected.Value != _IsSelected))
                {
                    _IsSelected = isSelected.Value;
                    _DisabledPropagateUpSelected = true;
                    OnPropertyChanged(nameof(IsSelected), raisePropertyChanged, updateState);
                    _DisabledPropagateUpSelected = false;
                }
                if ((isSelectable != null) && (isSelectable.Value != _IsSelectable))
                {
                    _IsSelectable = isSelectable.Value;
                    OnPropertyChanged(nameof(IsSelectable), raisePropertyChanged, updateState);
                }
                if ((isVisible != null) && (isVisible.Value != _IsVisible))
                {
                    _IsVisible = isVisible.Value;
                    OnPropertyChanged(nameof(IsVisible), raisePropertyChanged, updateState);
                }
            }
            else
            {
                if ((isEnabled != null) && (isEnabled.Value == _IsEnabled))
                {
                    _IsEnabled = !isEnabled.Value;
                    OnPropertyChanged(nameof(IsEnabled), raisePropertyChanged, updateState);
                }
                if ((isExpanded != null) && (isExpanded.Value == _IsExpanded))
                {
                    _IsExpanded = !isExpanded.Value;
                    OnPropertyChanged(nameof(IsExpanded), raisePropertyChanged, updateState);
                }
                if ((isSelected != null) && (isSelected.Value == _IsSelected))
                {
                    _IsSelected = !isSelected.Value;
                    _DisabledPropagateUpSelected = true;
                    OnPropertyChanged(nameof(IsSelected), raisePropertyChanged, updateState);
                    _DisabledPropagateUpSelected = false;
                }
                if ((isSelectable != null) && (isSelectable.Value == _IsSelectable))
                {
                    _IsSelectable = !isSelectable.Value;
                    OnPropertyChanged(nameof(IsSelectable), raisePropertyChanged, updateState);
                }
                if ((isVisible != null) && (isVisible.Value == _IsVisible))
                {
                    _IsVisible = !isVisible.Value;
                    OnPropertyChanged(nameof(IsVisible), raisePropertyChanged, updateState);
                }
            }
        }

        #endregion

        #endregion
        #region Constructors

        public TreeItem()
        {
            this.ChildItems = new TreeItemCollection();
        }

        #endregion
    }
}
